//-----------------------------------------------------------------------
// <copyright file="GetValue.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Mark Phillips</author>
// <email>v-mphill</email>
// <date>2004-05-27</date>
// <summary>Reads in an XML file and a value based on the provided XPath.
// </summary>
//-----------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks.Xml
{
    using System;
    using System.Xml;
    using System.Xml.XPath;
    using System.Globalization;
    using System.IO;
    using System.Text.RegularExpressions;
    using Microsoft.Build.Framework;

    /// <summary>
    /// Allows an XPath query to be executed on a set of specified XML documents.  Returns the selected values as a semicolon delimted string.
    /// If a single MatchValue is provided, each XPath query is matched against the same value.
    /// If a list of MatchValues are provided, the value in the list is matched against the Xml document with the corresponding index.  If a MatchValue list is provided with more than one item, the list must have the same number of items as there are in the Path parameter.
    /// </summary>
    /// <remarks>
    /// <code><![CDATA[
    /// <Xml.GetValue Path="path" XPath="xpath">
    ///     <Output TaskParameter="Results" ItemName="ResultsItem" />
    /// </Xml.GetValue>]]></code>
    /// <para>where:</para>
    /// <para><i>Path (Required)</i></para>
    /// <para>The path to the Xml document to apply the XPath query against.</para>
    /// <para><i>XPath (Required)</i></para>
    /// <para>The XPath query to be executed against the given Xml document.</para>
    /// <para><i>Results (Output)</i></para>
    /// <para>Stores the results of the query as a semicolon delimited string.</para>
    /// <para><i>MatchValue (Optional)</i></para>
    /// <para>The value to match against the XPath query result(s).  If one item is provided, the query for all specified Xml documents are matched against this single value.
    /// If a list of items is provided, the list must first have the same number of items as are in the Path parameter.  The respective value in the list is then used when matching against query results.</para>
    /// </remarks>
    /// <example>
    /// <code><![CDATA[
    /// <Project>
    ///     <Target Name="Test" >
    ///         <Xml.GetValue
    ///             Path="c:\tempXml.xml"
    ///             XPath="//books[@Value]" >
    ///             <Output TaskParameter="Results" ItemName="ResultsItem" />
    ///         </Xml.GetValue>
    ///     </Target>
    /// </Project>
    /// ]]></code>    
    /// </example>
    public class GetValue : TaskBase
    {
        private string[] path = new string[0];
        private string xPath = String.Empty;
        private string[] results = new string[0];
        private string[] matchValue = new string[0];
        private string[] nameSpaces = new string[0];

        /// <summary>
        /// Initializes a new instance of the GetValue class.
        /// </summary>
        public GetValue()
        {
        }

        /// <summary>
        /// Performs the action of this task.
        /// </summary>
        protected override void InternalExecute()
        {
            #region Execute code

            if (path.Length == 0)
            {
                throw new TaskException("Xml.GetValue.PathInvalid");
            }

            if (path.Length > 1 && (matchValue == null || matchValue.Length == 0))
            {
                throw new TaskException("Xml.GetValue.MatchInvalid");
            }

            if (matchValue != null && matchValue.Length > 1 && path.Length != matchValue.Length)
            {
                throw new TaskException("Xml.GetValue.MatchValueLength");
            }

            int pathIndex = 0;
            foreach (string checkPath in path)
            {
                if (System.IO.File.Exists(checkPath))
                {
                    // If the XPath is specified, search the document for the elements that have that
                    // pattern
                    //
                    if (xPath.Length == 0)
                    {
                        throw new TaskException("Xml.GetValue.XPathInvalid");
                    }

                    XPathDocument doc = new XPathDocument(checkPath);
                    XPathNavigator navigator = doc.CreateNavigator();
                    
                    XmlNamespaceManager ns = new XmlNamespaceManager(navigator.NameTable);

                    foreach (string s in nameSpaces)
                    {
                        string[] parts = s.Split('=');
                        if (parts.GetLength(0) != 2)
                            throw new TaskException("Xml.GetValue.XPathNsError");
                        ns.AddNamespace(parts[0], parts[1]);
                    }
                                           
                    navigator.MoveToChild(XPathNodeType.Element);
                    XPathNodeIterator iterator = null;
                    try
                    {
                        iterator = navigator.Select(xPath, ns);
                    }
                    catch (XPathException xpathEx)
                    {
                        throw new TaskException("Xml.GetValue.XPathError", new string[] { xPath, checkPath, xpathEx.Message });
                    }

                    if (iterator.Count > 0 && (matchValue == null || matchValue.Length == 0))
                    {
                        results = new string[iterator.Count];
                    }

                    int index = 0;
                    System.Text.StringBuilder sb = new System.Text.StringBuilder();

                    while (iterator.MoveNext())
                    {
                        string resultValue = iterator.Current.Value.ToString();

                        if (matchValue == null || matchValue.Length == 0)
                        {
                            results[index] = resultValue;
                        }
                        else
                        {
                            sb.Append(resultValue);
                            if (index + 1 != iterator.Count)
                            {
                                sb.Append(";");
                            }
                        }
                        index++;
                    }

                    if (matchValue != null && matchValue.Length > 0)
                    {
                        if (matchValue.Length == 1)
                        {
                            if (sb.ToString() != matchValue[0])
                            {
                                throw new TaskException("Xml.GetValue.NoMatch", new string[] { matchValue[0], xPath, checkPath });
                            }
                        }
                        else
                        {
                            if (sb.ToString() != matchValue[pathIndex])
                            {
                                throw new TaskException("Xml.GetValue.NoMatch", new string[] { matchValue[pathIndex], xPath, checkPath });
                            }
                        }
                    }
                }
                else
                {
                    throw new TaskException("File.NotFound", checkPath);
                }

                pathIndex++;
            }

            #endregion
        }

        [Required]
        public string[] Path
        {
            get
            {
                return (path == null ? new string[0] : path);
            }
            set
            {
                path = value;

                return;
            }
        }      

        /// <summary>
        /// Used to pass in namespaces and prefixes. These prefixes can then be used in the XPath expression
        /// use the a semicolon separated list of the form prefix=namespace
        /// eg.
        /// "xsd=http://www.w3.org/2001/XMLSchema;xsi=http://www.w3.org/2001/XMLSchema-instance"
        /// </summary>
        public string[] NameSpaces
        {
            get
            {
                return (nameSpaces == null ? new string[0] : path);
            }
            set
            {
                nameSpaces = value;

                return;
            }
        }

        [Required]
        public string XPath
        {
            get
            {
                return (xPath == null ? String.Empty : xPath);
            }
            set
            {
                xPath = value;

                return;
            }
        }

        public string[] MatchValue
        {
            get
            {
                return (matchValue == null ? new string[0] : matchValue);
            }
            set
            {
                matchValue = value;

                return;
            }
        }

        [Output]
        public string[] Results
        {
            get
            {
                return (this.results == null ? new string[0] : this.results);
            }
            set
            {
                this.results = value;

                return;
            }
        }

    }
}
